package com.rockbb.common.utilities.text;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.log4j.Logger;
import org.codehaus.jackson.JsonGenerationException;
import org.codehaus.jackson.JsonParseException;
import org.codehaus.jackson.map.ObjectMapper;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;

public class StringUtils 
{
	private static Logger logger = Logger.getLogger(StringUtils.class);
	private final static String[] FILE_SIZE_UNITS = new String[] {"B", "KB", "MB", "GB", "TB", "PB"};

	/**
	 * Get the formatted file size, e.g. 1.20MB, 32.5GB, etc.
	 * 
	 * @param length
	 * @return
	 */
	public static String formatFileSize(long length)
	{
		long divider = 1024L;
		double level = 1.0F;
		double numbers = 0F;
		for (int i = 0; i < FILE_SIZE_UNITS.length; i++)
		{
			double nextLevel = level * divider;
			if (length < nextLevel)
			{
				numbers = length / level;
				return String.format("%.2f " + FILE_SIZE_UNITS[i], numbers);
			}
			if (i < FILE_SIZE_UNITS.length - 1)
				level = nextLevel;
		}

		numbers = length / level;
		return String.format("%.2f " + FILE_SIZE_UNITS[FILE_SIZE_UNITS.length - 1], numbers);
	}

	public static String convertHTMLToText(String html)
	{
		Document doc = Jsoup.parse(html);

		if (doc != null)
		{
			return doc.text();
		}
		return "";
	}
	
	/**
	 * Convert the pure ASCII text to HTML for browser displaying.
	 * It will replace new lines with <br /> (or <P> when 2nd parameter is false)
	 * 
	 * @param textContent
	 * @return
	 */
	public static String converTextToHTML(String textContent, boolean useBR)
	{
		if (useBR)
		{
			return removeInvalidHtmlEntityInXml(textContent)
					.replaceAll("&(?!#?[a-z0-9]+;)", "&amp;").replaceAll("\\r?\\n", "<br />\n");
		}
		else
		{
			return "<p>" + removeInvalidHtmlEntityInXml(textContent)
					.replaceAll("&(?!#?[a-z0-9]+;)", "&amp;").replaceAll("(\\r?\\n)+", "</p>\n<p>") + "</p>";
		}
	}

	public static String removeInvalidHtmlEntityInXml(String textContent)
	{
		textContent = textContent.replaceAll("&nbsp;", " ")
				.replaceAll("&(lrm|shy);", "")
				.replaceAll("&(l|r)dquo;", "\"")
				.replaceAll("&laquo;", "«")
				.replaceAll("&raquo;", "»")
				.replaceAll("&(l|r)squo;", "'")
				.replaceAll("&(m|n)dash;", "-")
				.replaceAll("&(middot|bull);", "·")
				.replaceAll("&hellip;", "...")
				.replaceAll("&reg;", "(R)")
				.replaceAll("&copy;", "(C)")
				.replaceAll("<", "&lt;")
				.replaceAll(">", "&gt;")
				.replaceAll("&sup2;", "²")
				.replaceAll("&sup3;", "³")
				.replaceAll("&deg;", "&#176;")
				.replaceAll("&beta;", "β")
				.replaceAll("&oacute;", "ó")
				.replaceAll("&uacute;", "ú")
				.replaceAll("&acute;", "´")
				.replaceAll("&ccedil;", "ç")
				.replaceAll("&uuml;", "ü")
				.replaceAll("&times;", "&#215;")
				.replaceAll("&iexcl;", "¡")
				.replaceAll("&epsilon;", "ε")
				.replaceAll("&auml;", "ä")
				.replaceAll("&aacute;", "á")
				.replaceAll("&acirc;", "â")
				.replaceAll("&agrave;", "à")
				.replaceAll("&ocirc;", "ô")
				.replaceAll("&icirc;", "î")
				.replaceAll("&brvbar;", "¦")
				.replaceAll("&ge;", "≥");
		return textContent;
	}

	/**
	 * Convert string to specified encoding. It will return null upon any exceptions
	 * 
	 * @param string
	 * @param targetEncoding
	 * @return
	 */
	public static String convertEncoding(String string, String sourceEncoding, String targetEncoding)
	{
		try
		{
			byte[] bytes = string.getBytes(sourceEncoding);
			return new String(bytes, targetEncoding);
		}
		catch (UnsupportedEncodingException e)
		{
			logger.info("UnsupportedEncodingException: " + targetEncoding);
		}
	    return null;
	}

	public static String implode(Collection<String> collection, String separator)
	{
		if (collection == null )
			return null;
		if (collection.size() == 0) 
			return "";

		StringBuffer sb = new StringBuffer();
		Iterator<String> iterator = collection.iterator();
		while (iterator.hasNext())
		{
			sb.append(iterator.next());
			if (iterator.hasNext()) sb.append(separator);
		}
		return sb.toString();
	}

	/**
	 * 合并一個字符串數組, 用預置的字符做分隔
	 * 
	 * @param array
	 * @param separator
	 * @return
	 */
	public static String implode(String[] array, String separator)
	{
		if (array == null )
			return null;
		if (array.length == 0) 
			return "";
		if (array.length == 1) 
			return array[0];

		StringBuffer sb = new StringBuffer();
		sb.append(array[0]);
		for (int i=1; i<array.length; i++)
		{
			sb.append(separator);
			sb.append(array[i]);
		}
		return sb.toString();
	}

	/**
	 * Split a string by calling String.split(), if the sourceString is null, it returns null
	 * 
	 * @param sourceString
	 * @param splitter
	 * @return
	 */
	public static String[] split(String sourceString, String splitter)
	{
		if (sourceString == null)
			return null;
		else if (sourceString.length() == 0)
			return new String[0];
		else
			return sourceString.split(splitter);
	}

	/**
	 * Append the given String to the given String array, returning a new array
	 * consisting of the input array contents plus the given String.
	 * 
	 * @param arr the array to append to
	 * @param str the String to append
	 * @return the new array
	 */
	public static String[] addStringToArray(String[] arr, String str) 
	{
		String[] newArr = new String[arr.length + 1];
		System.arraycopy(arr, 0, newArr, 0, arr.length);
		newArr[arr.length] = str;
		return newArr;
	}

	/**
	 * 为一个URL地址增加参数, 会自动判断是否使用'?'号连接
	 * 
	 * @param url
	 * @param params
	 * @return
	 */
	public static String appendParametersToURL(String url, List<String[]> params, String encoding)
	{
		if (url == null) url="";
		if (params == null || params.size() == 0) return url;

		StringBuffer sb = new StringBuffer();
		sb.append(url);
		char joiner;
		if (url.indexOf('?') == -1)
			joiner = '?';
		else
			joiner = '&';

		for (String[] pair: params)
		{
			if (pair.length < 2) continue;
			sb.append(joiner);
			sb.append(pair[0]+"=");
			try 
			{
				sb.append(URLEncoder.encode(pair[1], encoding));
			}
			catch(UnsupportedEncodingException e) 
			{
				logger.info("UnsupportedEncodingException");
				logger.debug(e.getMessage(), e);
			}
			joiner = '&';
		}
		return sb.toString();
	}

	public static boolean isEmailAddress(String email_address)
	{
		// regular expression [\w\.-]+@([\w-]+\.)+[a-z]{2,3}
		Pattern p1 = Pattern.compile("[\\w\\.-]+@([\\w-]+\\.)+[a-z]{2,5}");
        Matcher m = p1.matcher(email_address);
        return m.matches();
	}

	public static boolean isSingleByte(char c) 
	{
		int k = 0x80;
		return c / k == 0 ? true : false;
	}

	/**
	 * Return the actual length of the input string when it is displayed in the screen. 
	 * English char = 1, Chinese Char = 2
	 * 
	 * @param str
	 * @return
	 */
	public static int stringScreenLength(String str)
	{
		if (str == null || str.length() == 0) return 0;
		char[] units = str.toCharArray();
		int length = 0;
		for (char c : units)
		{
			length ++;
			if (!isSingleByte(c))
				length ++;
		}
		return length;
	}

	/**
	 * Shorten the string by specified width for terminal, without breaking the Chinese characters
	 * One Chinese character is treated as two English characters
	 * 
	 * @param str
	 * @param length
	 * @return
	 */
	public static String shortenString(String str, int width) 
	{
		if (str == null || str.length() == 0 || str.getBytes().length <= width) return str;
		char[] units = str.toCharArray();
		StringBuffer sb = new StringBuffer();
		int ac_length = 0;
		for (char c : units)
		{
			sb.append(c); ac_length ++;
			if (!isSingleByte(c))
				ac_length ++;
			if (ac_length >= width)
				break;
		}
		return sb.toString();
	}

	/**
	 * Wrap the input content by specified width, without breaking the multi-byte characters such as Chinese
	 * One Chinese character is treated as two English characters
	 * 
	 * @param content
	 * @param width
	 * @param withEmptyLine
	 * @return
	 */
	public static String wrapStringForTerminalDisplaying(String content, int width, boolean withEmptyLine)
	{
		// If the content is empty, return without change
		if (content == null || content.length() == 0) return content;

		String[] contentLines = content.split("\\r?\\n");

		StringBuilder sb = new StringBuilder();

		// Go through all chars in each line
		for (int i=0; i<contentLines.length; i++)
		{
			// If it is an empty line and withEmptyLine is true, add it back, because it will never run into the cycle below
			if (contentLines[i].length() == 0 && withEmptyLine) sb.append("\n");

			char[] units = contentLines[i].toCharArray();
			StringBuilder lineBuffer = new StringBuilder();
			int pos = 0;
			int ac_length = 0;
			for (int j=pos; j<units.length ; j++)
			{
				sb.append(units[j]); ac_length ++;
				if (!isSingleByte(units[j]))
					ac_length ++;
				// If current buffer length has exceeded the width, or has reached the end, 
				// append a new line in the output and reset the buffer length
				if (ac_length >= width || j==units.length - 1)
				{
					sb.append(lineBuffer).append("\n");
					lineBuffer.setLength(0);
					ac_length = 0;
				}
			}
		}

		return sb.toString();
	}
	
	/**
	 * Shorten the string by its nature length (the length of Chinese character equals one)  
	 * 
	 * @param str
	 * @param length
	 * @return
	 */
	public static String shortenStringNative(String str, int length)
	{
		if (str == null || str.length() == 0 || str.length() <= length) return str;
		return str.substring(0, length);
	}

	public static boolean arrayContains(String[] array, String string)
	{
		boolean flag = false;
		if (array != null && string !=null && array.length > 0)
		{
			for (String item : array)
			{
				if (item.equals(string))
				{
					flag = true;
					break;
				}
			}
		}
		return flag;
	}

	public static List<Integer> extractIntegerList(String listString) {
		List<Integer> list = new ArrayList<Integer>();
		if (listString == null) return list;

		ObjectMapper mapper = JacksonMapper.getInstance();
		try {
			list = mapper.readValue(listString, list.getClass());
		} catch (JsonParseException e) {
			// Do nothing
		} catch (IOException e) {
			// Do nothing
		}
		return list;
	}

	public static String compressIntegerList(List<Integer> list) {
		String listString = "";
		ObjectMapper mapper = JacksonMapper.getInstance();
		try {
			listString = mapper.writeValueAsString(list);
		} catch (JsonGenerationException e) {
			// Do nothing
		} catch (IOException e) {
			// Do nothing
		}
		return listString;
	}

	public static List<String> extractStringList(String listString) {
		List<String> list = new ArrayList<String>();
		if (listString == null) return list;

		ObjectMapper mapper = JacksonMapper.getInstance();
		try {
			list = mapper.readValue(listString, list.getClass());
		} catch (JsonParseException e) {
			// Do nothing
		} catch (IOException e) {
			// Do nothing
		}
		return list;
	}

	public static String compressStringList(List<String> list) {
		String listString = "";
		ObjectMapper mapper = JacksonMapper.getInstance();
		try {
			listString = mapper.writeValueAsString(list);
		} catch (JsonGenerationException e) {
			// Do nothing
		} catch (IOException e) {
			// Do nothing
		}
		return listString;
	}

	/**
	 * Filter out the illegal characters from a UTF-8 string
	 * 
	 * @param inStr
	 * @return
	 */
	public static String utf8_clean_string(String inStr) {
		String outStr = "";
		outStr = inStr.toLowerCase().replaceAll(" {2,}", " ");
		try {
			outStr = new String(outStr.getBytes("utf-8"), "iso8859-1");
			outStr = outStr.replaceAll("(?:[\\x00-\\x1F\\x7F]+|(?:\\xC2[\\x80-\\x9F])+)", "");
			outStr = new String(outStr.getBytes("iso8859-1"), "utf-8");
		} catch (UnsupportedEncodingException e) {
			// Do nothing
		}
		return outStr;
	}

	public static String readInputStream(InputStream is)
	{
		BufferedReader br = null;
		StringBuilder sb = new StringBuilder();

		try
		{
			br = new BufferedReader(new InputStreamReader(is));
			String line = null;
			while ((line = br.readLine()) != null)
			{
				sb.append(line);
			}
		}
		catch (IOException e)
		{
			logger.info("IOException in reading");
		}
		finally
		{
			try
			{
				if (br != null) br.close();
			}
			catch (IOException e)
			{
				logger.info("IOException in closing");
			}
		}
		return sb.toString();
	}

	public static String readFile(String path, String charset)
	{
		BufferedReader br = null;
		StringBuilder sb = new StringBuilder();

		try
		{
			br = new BufferedReader(new InputStreamReader(new FileInputStream(path), charset));
			String line;
			while ((line = br.readLine()) != null)
			{
				sb.append(line+"\n");
			}
		}
		catch (IOException e)
		{
			logger.info("IOException in reading");
		}
		finally
		{
			try
			{
				if (br != null) br.close();
			}
			catch (IOException e)
			{
				logger.info("IOException in closing");
			}
		}
		return sb.toString();
	}

	public static void main(String[] args)
	{
		System.out.println(formatFileSize(8916100448256L));
		Set<String> set = new HashSet<String>();
		for (int i = 0; i < 12; i ++)
		{
			set.add(System.currentTimeMillis()+i+"");
		}
		String str = implode(set, ", ");
		System.out.println(str);

		/*StoryBean story = StoryService.getStory(110613);

		String text = convertHTMLToText(story.getContentXhtml());
		System.out.println(text);*/
		
		/*try {
			String s = new String(new byte[]{
					(byte)0x61,//a
				(byte)0xe4,(byte)0xb8,(byte)0xad,//中
				(byte)0xc2,(byte)0x9f,//
				(byte)0x62//b
				},"utf-8");
			System.out.println(Arrays.toString(s.getBytes("iso8859-1")));
			System.out.println(s);
			String tmp = utf8_clean_string(s);
			System.out.println(Arrays.toString(tmp.getBytes("iso8859-1")));
			System.out.println(tmp);
		} catch(Exception e) {
			e.printStackTrace();
		}

		if (isEmailAddress("milton.lai-zh_CN_gb2312@caf-no_a3.prnasia.com")) System.out.println("1true");
		if (isEmailAddress("milton.lai-zh_CN_gb2312-caf-no_a3.prnasia.com")) System.out.println("2false");
		if (isEmailAddress("http://caf-no_a3.prnasia")) System.out.println("3false");
		String str="今天公佈截至2011年9月30日止三個月（「回顧期內」）之第三季度業績，並宣佈在期內分別與內蒙古尚華置業集團有限公司（「尚華置業」）及鄂爾多斯市正鼎房地產開發有限責任公司（「正鼎房地產」）達成重大交易協議，合同額合共達逾人民幣750,000,000元。";
		int size = str.getBytes().length;
		for (int i=size; i>=0; i--) {
			System.out.println(shortenString(str, i));
		}

		//String test = "testing 上海2012年3月21日电 /美通社亚洲/ -- 由商会网( http://www.netcoc.com)发起并主办 < TESTING 的“2012沃尔沃 S80L 探索中国商业潜力 -- 商会中国行”系列活动，即将于2012年3月15日行至青岛。活动沿着商帮的足迹，走访当代中国十大商帮，对话会长领袖，探寻中国潜力行业，为中国经济新增长抛砖引玉。\r\n\r\n\r\n"
		//	+"整个“商会中国行”系列活动将0002NASDAQ-NMS@FISV NASDAQ-NNS@dd5555以“新旧激荡中前行”为主线，剖析十大商帮城市新老产业0002NASDAQ-NMS@FISV NASDAQ-NNS@dd5555在同一经济环境中不同的成长发展模式，探索出中国经济新的增长潜力。此次青岛站的主题为鲁商的“海陆”思考。\n\n\n\n"
		//	+ "鲁商，在中国历史上兴起较早，和徽商、晋商、浙商、粤商一起，被公认为是中国五大著名商帮。鲁商是山东的商人精英群体，有人称山东商帮，始于商周，形成于春秋战国，成熟于两汉，在此后的不同历史阶段，又有所发展与完善。鲁商是山东经济社会宝贵的人文资源，伴随着“鲁商”概念迅速叫响全国，鲁商已成为国人解读山东经济、山东速度、山东精神的一种范本，成为山东的一张崭新名片，鲁商崛起的意义已超越其经济领域，成为山东社会文明进程的一个大事件。\r\n";
		
		String test = "page=24&ipp=20&tx_dynalist_pi1%5bpar%5d=YToxOntzOjE6IkwiO3M6MToiMCI7fQ== "
				+")”，友达光电\"( http://www.auo.com/?sn=103&lang=zh-CHS )今（20）日发表光电业首见的创新废水全回收系统技术，"
				+"透过回收再回收的方式，预期可成功达到废水零排放目标。友达多年来致力水资源回收再利用，所有厂区用水回收率均达八成以上，分别获得美国绿建筑协会 "
				+"LEED 金级与白金级认证的台湾台中及后里8.5代厂，用水回收率更高达九成以上。现今在水资源处理技术更有关键性突破，开发出光电制造业第一个废水全回收解决方案，朝水资源100%回收的目标努力。</P>"
				+ "<P>由于人文、气候因素造成的水资源匮乏，联合国于1992年起订定每年3月22日为“世界水资源日( http://www.unwater.org/water-cooperation-"
				+"2013/events/worldwide-events/events-list/en/?page=24&ipp=20&tx_dynalist_pi1%";
		test = "<a>AT&T &#123; &ndash;  &lt; $lquot; < aalsdkdlsd /> & 1";
		String out = test.replaceAll( "&([^;]+(?!(?:\\w|;)))", "&amp;$1" );
		System.out.println(out);

		System.out.println(wrapStringForTerminalDisplaying(test, 80, true));

		System.out.println(converTextToHTML(test, false));*/

	}
}
